var kurento = require("kurento-client");
var path = require("path");
var fs = require("fs");
var shelljs = require('shelljs');

class TKms {
    constructor() {
        this.config = {
            clientId: ['c', (new Date()).getTime(), parseInt(100 * Math.random())].join(""),
            version: "kms.v21",
            recorderPath: "/data/rec",
            kmsUrl: "wss://wj.hhdata.cn:10104/kurento",
            clientKmsUrl: "wss://wj.hhdata.cn:10104/kurento",
            peerOptions: {
                configuration: {
                    "iceServers": [{
                            url: "stun:39.171.53.53:3478"
                        },
                        {
                            urls: ["turn:39.171.53.53:3478"],
                            username: "kurento",
                            credential: "kurento"
                        }
                    ]
                },
                mediaConstraints: {
                    audio: true,
                    video: {
                        width: { min: 1280, ideal: 1920, max: 1920 },
                        height: { min: 720, ideal: 1080, max: 1080 }
                    },
                }
            }
        };
        this.kmsObjs = {};
        this.kmsClient = null;
    };
    getClient() {
        var me = this;
        return new Promise((success, faild) => {
            try {
                if (me.kmsClient && me.kmsClient != null) {
                    success(me.kmsClient);
                } else {
                    kurento(me.config.kmsUrl, function(erClient, client) {
                        if (erClient && erClient != null) {
                            success(null);
                        } else {
                            console.log('connect to kms :', me.config.kmsUrl);
                            client.on('disconnect', function() {
                                delete me['kmsClient'];
                            });
                            me.kmsClient = client;
                            client.getServerManager().then(_svr => {
                                me.kmsClient.svr = _svr;
                                _svr.on('ObjectDestroyed', function(event) {
                                    console.log('freeObj', event);
                                    var kmsId = event.objectId;
                                    try {
                                        _svr.removeTag(kmsId, function(eeRemove) {
                                            console.log('remove ', kmsId, eeRemove);
                                        });
                                    } catch (eer) {
                                        console.log(eer);
                                    }
                                });
                                success();
                            })
                        }
                    });
                }
            } catch (er) {
                console.log(er);
            }
        });
    };
    getTags() {
        var me = this;
        var res = {};
        return new Promise((success, faild) => {
            try {
                if (me.kmsClient && me.kmsClient != null) {
                    var svr = me.kmsClient.svr;
                    svr.getTags().then(_tags => {
                        _tags.forEach(t => {
                            var flag = t.key.indexOf('.MediaPipeline') > 0 && t.value.indexOf('.MediaPipeline') > 0;
                            if (flag) {
                                try {
                                    var info = JSON.parse(t.value);
                                    if (info.version && info.version == me.config.version) {
                                        res[t.key] = info;
                                    }
                                } catch (eer) {
                                    console.log(eer);
                                }
                            }
                        });
                        success(res);
                    });
                } else {
                    success(res);
                }
            } catch (er) {
                console.log(er);
                success(res);
            }
        });
    };
    getAllKmsObjs() {
        var me = this;
        var res = {};
        return new Promise((success, faild) => {
            try {
                var objs = [];
                if (me.kmsClient && me.kmsClient != null) {
                    var svr = me.kmsClient.svr;
                    svr.getPipelines().then(ps => {
                        var loadItem = () => {
                            if (ps.length > 0) {
                                var p = ps.shift();
                                objs.push(p);
                                p.getChildren().then(childs => {
                                    childs.forEach(o => {
                                        objs.push(o);
                                    })
                                    loadItem();
                                });
                            } else {
                                objs.forEach(o => {
                                    res[o.id] = o;
                                });
                                success(res);
                            }
                        };
                        loadItem();
                    });
                } else {
                    success(res);
                }
            } catch (er) {
                console.log(er);
                success(res);
            }
        });
    };
    clearTags() {
        var me = this;
        var res = {};
        return new Promise((success, faild) => {
            try {
                var objs = {};
                me.getClient().then(_ => {
                    return me.getAllKmsObjs();
                }).then(_objs => {
                    objs = _objs;
                    return me.kmsClient.svr.getTags();
                }).then(_tags => {
                    var removeTags = _tags.filter(tag => {
                        var flag = tag.key.indexOf(".MediaPipeline") && tag.value.indexOf(".MediaPipeline");
                        if (flag) {
                            var info = JSON.parse(tag.value);
                            var objFlag = !(objs[tag.key] && objs[tag.key] != null);
                            flag = info.version == me.config.version && objFlag;
                            return flag;
                        } else {
                            return false;
                        }
                    });
                    var freeItem = () => {
                        if (removeTags.length > 0) {
                            tagKey = removeTags.shift().key;
                            me.kmsClient.svr.removeTag(tagKey).then(_ => {
                                freeItem();
                            });
                        }
                    };
                    freeItem();
                });

            } catch (er) {
                console.log(er);
                success(res);
            }
        });
    };
    freeKmsObjs(objs) {
        var me = this;
        return new Promise((success, faild) => {
            try {
                if (objs && objs != null && objs.length > 0) {
                    var index = 0;
                    var freeItem = () => {
                        if (index < objs.length) {
                            objs[index].release().then(_ => {
                                index++;
                                freeItem();
                            });
                        }
                    };
                    freeItem();
                } else {
                    success();
                }
            } catch (er) {
                console.log(er);
                success();
            }
        });
    };
    registObj(obj, code, clientId, info) {
        var me = this;
        return new Promise((success, faild) => {
            try {
                if (obj && obj != null) {
                    var objInfo = info || {};
                    objInfo.kmsCode = code;
                    objInfo.kmsId = obj.id;
                    var arr = obj.id.split(".");
                    objInfo.kmsType = arr[arr.length - 1];
                    objInfo.clientId = clientId && clientId != null && clientId.length > 0 ? clientId : me.config.clientId;
                    objInfo.version = me.config.version;
                    var tagContent = JSON.stringify(objInfo);
                    me.kmsClient.svr.addTag(obj.id, tagContent).then(_ => {
                        obj.tag = objInfo;
                        me.kmsObjs[obj.id] = obj;
                        success(obj);
                    })
                } else {
                    success(obj);
                }
            } catch (er) {
                console.log(er);
                success(obj);
            }
        });
    };
    getClientObjs(clientId) {
        var me = this;
        var res = [];
        return new Promise((success, faild) => {
            try {
                var tags = [];
                me.getTags().then(_tags => {
                    tags = Object.values(_tags).filter(t => {
                        var flag = false;
                        try {
                            if (t.clientId && t.clientId == clientId) {
                                flag = true;
                            }
                        } catch (eer) {
                            flag = false;
                        }
                        return flag;
                    });
                    return me.getAllKmsObjs();
                }).then(objs => {
                    res = Object.values(objs).filter(o => {
                        return tags[o.id] && tags[o.id] != null;
                    })
                    success(res);
                });
            } catch (er) {
                console.log(er);
                success(res);
            }
        });
    };
    //--------------------- 
    getChannels() {
        var me = this;
        var res = {};
        return new Promise((success, faild) => {
            try {
                var tags = {};
                me.getTags().then(_tags => {
                    tags = _tags;
                    return me.getAllKmsObjs();
                }).then(objs => {
                    objs.forEach(o => {
                        var tag = tags[o.id];
                        if (tag && tag != null) {
                            o.tag = tag;
                            if (tag.modelType == "channel") {
                                res[o.id] = o;
                            } else if (tag.modelType == "seat") {
                                var chKmsId = o.id.split("/")[0];
                                if (objs[chKmsId] && objs[chKmsId] != null) {
                                    if (!(objs[chKmsId].seats)) {
                                        objs[chKmsId].seats = {};
                                    }
                                    objs[chKmsId].seats[tag.kmsCode] = o;
                                }
                            } else if (tag.modelType == "end") {
                                var chKmsId = o.id.split("/")[0];
                                if (objs[chKmsId] && objs[chKmsId] != null) {
                                    if (!(objs[chKmsId].ends)) {
                                        objs[chKmsId].ends = {};
                                    }
                                    objs[chKmsId].ends[tag.kmsCode] = o;
                                }
                            }
                        }
                    });
                    success(res);
                });
            } catch (er) {
                console.log(er);
                success(res);
            }
        });
    };
    loadChannel(channelCode, autoCreate) {
        var me = this;
        return new Promise((success, faild) => {
            try {
                var createNew = () => {
                    if (autoCreate) {
                        me.kmsClient.create("MediaPipeline").then(_pipe => {
                            return me.registObj(_pipe, channelCode, me.config.clientId, { modelType: "channel", channelCode: channelCode });
                        }).then(pipe => {
                            success(pipe);
                        });
                    } else {
                        success(null);
                    }
                };
                me.getClient().then(client => {
                    return me.getTags();
                }).then(tags => {
                    var pipeTag = Object.values(tags).find(t => {
                        return t.kmsType == "MediaPipeline" && t.kmsCode == channelCode;
                    });
                    if (pipeTag && pipeTag != null && pipeTag.kmsId && pipeTag.kmsId != null && pipeTag.kmsId.length > 0) {
                        me.kmsClient.getMediaobjectById(pipeTag.kmsId, function(err, pipe) {
                            if (pipe && pipe != null) {
                                pipe.tag = pipeTag;
                                success(pipe);
                            } else {
                                var items = Object.values(tags).filter(t => {
                                    return t.kmsId.indexOf(pipeTag.kmsId) > 0;
                                });
                                var freeItem = () => {
                                    if (items.length > 0) {
                                        var kmsId = items.shift();
                                        me.kmsClient.svr.removeTag(kmsId).then(_ => {
                                            freeItem();
                                        })
                                    } else {
                                        createNew();
                                    }
                                };
                                freeItem();
                            }
                        });
                    } else {
                        createNew();
                    }
                });
            } catch (er) {
                console.log(er);
                success();
            }
        });
    };
    closeChannel(channelCode) {
        var me = this;
        return new Promise((success, faild) => {
            try {
                me.getClient().then(client => {
                    return me.getTags();
                }).then(tags => {
                    var pipeTag = Object.values(tags).find(t => {
                        return t.kmsType == "MediaPipeline" && t.kmsCode == channelCode;
                    });
                    if (pipeTag && pipeTag != null && pipeTag.kmsId && pipeTag.kmsId != null && pipeTag.kmsId.length > 0) {
                        var pipeId = pipeTag.kmsId;
                        me.kmsClient.getMediaobjectById(pipeId).then(pipe => {
                            if (pipe && pipe != null) {
                                pipe.getChildren().then(objs => {
                                    objs.push(pipe);
                                    me.freeKmsObjs(objs).then(_ => {
                                        success();
                                    })
                                })
                            } else {
                                success();
                            }
                        })
                    } else {
                        success();
                    }
                });
            } catch (er) {
                console.log(er);
                success();
            }
        });
    };
    loadChannelSeats(channelCode, seatCodes, autoCreate) {
        var me = this;
        var seats = {};
        var channel = null;

        return new Promise((success, faild) => {
            try {
                me.loadChannel(channelCode, autoCreate).then(chPipe => {
                    channel = chPipe;
                    channel.seats = seats;
                    if (chPipe && chPipe != null) {
                        var pts = [];
                        chPipe.getChildren().then(items => {
                            pts = items.filter(item => {
                                return item.id.indexOf(".PassThrough") > 0;
                            });
                            return me.getTags();
                        }).then(_tags => {
                            pts.forEach(pt => {
                                var tag = _tags[pt.id];
                                if (tag && tag != null) {
                                    pt.tag = tag;
                                    seats[tag.kmsCode] = pt;
                                }
                            });
                            if (autoCreate) {
                                var items = seatCodes.filter(s => {
                                    var flag = seats[s] && seats[s] != null;
                                    return !flag;
                                });
                                var loadItem = () => {
                                    if (items.length > 0) {
                                        var seatCode = items.shift();
                                        chPipe.create("PassThrough").then(pt => {
                                            return me.registObj(pt, seatCode, me.config.clientId, {
                                                modelType: "seat",
                                                channelCode: channelCode,
                                                seatCode: seatCode
                                            });
                                        }).then(_pt => {
                                            seats[seatCode] = _pt;
                                            loadItem();
                                        });
                                    } else {
                                        success(channel);
                                    }
                                };
                                loadItem();
                            } else {
                                success(channel);
                            }
                        })
                    } else {
                        success(channel);
                    }
                })
            } catch (er) {
                console.log(er);
                success(channel);
            }
        });
    };
    createChannelEnd(channelCode, endCode, clientId) {
        var me = this;
        return new Promise((success, faild) => {
            try {
                me.loadChannel(channelCode, true).then(chPipe => {
                    if (chPipe && chPipe != null) {
                        chPipe.create("WebRtcEndpoint").then(_end => {
                            return me.registObj(_end, endCode, clientId, {
                                modelType: "end",
                                channelCode: channelCode,
                                endCode: endCode,
                                clientId: clientId
                            });
                        }).then(end => {
                            success(end);
                        })
                    } else {
                        success();
                    }
                })
            } catch (er) {
                console.log(er);
                success();
            }
        });
    };
    connectMedia(srcId, targetId, mediaType) {
        var me = this;
        return new Promise((success, faild) => {
            var res = {
                srcFlag: 0,
                target: 0,
                successFlag: 0
            };
            var objs = {
                src: null,
                target: null
            }
            try {
                me.getClient().then(client => {
                    client.getMediaobjectById(srcId, function(er0, src) {
                        if (src && src != null) {
                            res.srcFlag = 1;
                            objs.src = src;
                            client.getMediaobjectById(targetId, function(er1, target) {
                                if (target && target != null) {
                                    res.target = 1;
                                    objs.target = target;
                                    var media = mediaType && mediaType != null && mediaType.length > 0 ? mediaType : "";
                                    if (media == "") {
                                        objs.src.connect(target, function(er2) {
                                            console.log(objs.src.id, '-->', target.id, ':', er2);
                                            res.successFlag = !(er2 && er2 != null);
                                            success(res);
                                        })
                                    } else {
                                        objs.src.connect(target, media, function(er2) {
                                            res.successFlag = !(er2 && er2 != null);
                                            success(res);
                                        })
                                    }
                                } else {
                                    success(res);
                                }
                            })
                        } else {
                            success(res);
                        }
                    });
                })
            } catch (er) {
                console.log(er);
                success();
            }
        });
    };
    // endseatCodes:[
    //      {endCode:'e01' , seatCode:'s01' , type:1-pusher,0-pull }   
    // ]
    loadChannelSeatEnds(channelCode, endSeatcCodes, clientId, autoCreate) {
        var me = this;
        return new Promise((success, faild) => {
            try {
                var seatCodes = endSeatcCodes.map(i => {
                    return i.seatCode;
                });
                me.loadChannelSeats(channelCode, seatCodes).then(ch => {
                    if (ch && ch != null) {
                        ch.ends = {};
                        var index = 0;
                        var loadItem = () => {
                            if (index < endSeatcCodes.length) {
                                var info = JSON.parse(JSON.stringify(item));
                                info.modelType = "end";
                                ch.create("WebRtcEndpoint").then(_end => {
                                    _end.seat = ch.seats[info.seatCode];
                                    ch.ends[info.endCode] = _end;
                                    return me.registObj(_end, info.endCode, clientId, info);
                                }).then(_ => {
                                    var src = info.type == 1 ? ch.ends[info.endCode] : ch.seats[info.seatCode];
                                    var target = info.type == 0 ? ch.ends[info.endCode] : ch.seats[info.seatCode];
                                    if (src && src != null && target && target != null) {
                                        src.connect(end, function(erConn) {
                                            index++;
                                            loadItem();
                                        })
                                    } else {
                                        index++;
                                        loadItem();
                                    }
                                });
                            } else {
                                success(ch);
                            }
                        };
                        loadItem();
                    } else {
                        success(ch);
                    }
                })
            } catch (er) {
                console.log(er);
                success(channel);
            }
        });
    };
    startRecord(srcId, recFn) {
        var me = this;
        var res = {
            ac: 'record',
            srcId: srcId,
            path: recFn,
            recorder: null,
            status: 0
        }
        return new Promise((success, faild) => {
            try {
                var arr = srcId.split("/");
                var pipeId = arr[0];
                var pipe = null;
                var src = null;
                var recorder = null;
                me.getClient().then(client => {
                    client.getMediaobjectById(srcId, function(erSrc, _src) {
                        if (_src && _src != null) {
                            src = _src;
                            client.getMediaobjectById(pipeId).then(_pipe => {
                                pipe = _pipe;
                                var fn = path.join(me.config.recorderPath, recFn);
                                var fnPath = path.parse(fn).dir;
                                if (!fs.existsSync(fnPath)) {
                                    shelljs.mkdir('-p', fnPath);
                                    if (fs.existsSync(fnPath)) {
                                        shelljs.chmod(777, fn);
                                    }
                                }
                                if (fs.existsSync(fnPath)) {
                                    var opts = {
                                        stopOnEndOfStream: true,
                                        uri: 'file://' + fn
                                    };
                                    pipe.create("RecorderEndpoint", opts).then(_recorder => {
                                        recorder = _recorder;
                                        recorder.on("Stopped", function(evData) {
                                            recorder.release();
                                        });
                                        var info = {
                                            modelType: "recorder",
                                            srcId: srcId,
                                            recFn: recFn,
                                            startTm: (new Date()).getTime()
                                        };
                                        return me.registObj(recorder, recFn, info, me.clientId);
                                    }).then(_ => {
                                        return src.connect(recorder);
                                    }).then(_ => {
                                        return recorder.record();
                                    }).then(_ => {
                                        res.recorder = recorder;
                                        res.success = 1;
                                        success(res);
                                    });
                                } else {
                                    success(res);
                                }
                            })
                        } else {
                            success(res);
                        }
                    })
                });
            } catch (er) {
                console.log(er);
                success(res);
            }
        });
    };
    stopRecord(recorderId) {
        var me = this;
        return new Promise((success, faild) => {
            try {
                me.getClient().then(client => {
                    client.getMediaobjectById(recorderId, function(erSrc, recorder) {
                        if (recorder && recorder != null) {
                            recorder.stopAndWait().then(_ => {
                                success();
                            });
                        } else {
                            success();
                        }
                    })
                });
            } catch (er) {
                console.log(er);
                success();
            }
        });
    };
    //----------------------------------------------------

    regist2WebSvr(webSvr) {
        var me = this;
        if (webSvr && webSvr != null && webSvr.registRoute) {
            var respJson = function(result, response) {
                try {
                    response.send(JSON.stringify(result));
                } catch (eer) {
                    console.log(eer);
                }
            };
            webSvr.registRoute("kms", "v21", "getConfig", function(result, request, response) {
                try {
                    result.data = {
                        kmsUrl: me.config.clientKmsUrl,
                        version: me.config.version,
                        peerOptions: me.config.peerOptions
                    };
                    respJson(result, response);
                } catch (err) {
                    console.log(err);
                    respJson(result, response);
                }
            });
            webSvr.registRoute("kms", "v21", "loadChannel", function(result, request, response) {
                try {
                    var channelCode = result.ps.channelCode;
                    //var seatCodesJson = result.ps.seatCodesJson;
                    var seatCodes = result.ps.seatCodes.split(",");
                    var autoCreate = result.ps.autoCreate;
                    autoCreate = autoCreate != 0 ? 1 : 0;
                    me.loadChannelSeats(channelCode, seatCodes, autoCreate).then(ch => {
                        var res = {
                            channel: ch.tag,
                            seats: {}
                        };
                        Object.values(ch.seats).forEach(seat => {
                            res.seats[seat.tag.kmsCode] = seat.tag;
                        });
                        result.data = res;
                        respJson(result, response);
                    });
                } catch (err) {
                    console.log(err);
                    respJson(result, response);
                }
            });
            webSvr.registRoute("kms", "v21", "closeChannel", function(result, request, response) {
                try {
                    var channelCode = result.ps.channelCode;
                    me.closeChannel(channelCode).then(_ => {
                        result.data = { ac: 'closechannel' };
                        respJson(result, response);
                    });
                } catch (err) {
                    console.log(err);
                    respJson(result, response);
                }
            });
            webSvr.registRoute("kms", "v21", "loadChannelSeatEnds", function(result, request, response) {
                try {
                    var channelCode = result.ps.channelCode;
                    var endSeatcCodesJson = result.ps.endSeatcCodesJson;
                    var endSeatcCodes = JSON.parse(endSeatcCodesJson);
                    var clientId = result.ps.clientId;
                    var autoCreate = result.ps.autoCreate;
                    autoCreate = autoCreate != 0 ? 1 : 0;
                    me.loadChannelSeatEnds(channelCode, endSeatcCodes, clientId, autoCreate).then(ch => {
                        var res = {
                            channel: ch.tag,
                            seats: {},
                            ends: {}
                        };
                        Object.keys(ch.seats).forEach(seat => {
                            res.seats[seat.tag.kmsCode] = seat.tag;
                        });
                        Object.keys(ch.ends).forEach(end => {
                            if (end.seat && end.seat != null) {
                                end.tag.seatKmsId = end.seat.id;
                                end.tag.seatCode = end.seat.tag.kmsCode;
                            }
                            res.ends[end.tag.kmsCode] = end.tag;
                        });
                        result.data = res;
                        respJson(result, response);
                    });
                } catch (err) {
                    console.log(err);
                    respJson(result, response);
                }
            });
            webSvr.registRoute("kms", "v21", "createChannelEnd", function(result, request, response) {
                try {
                    var channelCode = result.ps.channelCode;
                    var endCode = result.ps.endCode;
                    var clientId = result.ps.clientId;
                    me.createChannelEnd(channelCode, endCode, clientId).then(end => {
                        result.data = end.tag;
                        respJson(result, response);
                    });
                } catch (err) {
                    console.log(err);
                    respJson(result, response);
                }
            });
            webSvr.registRoute("kms", "v21", "getChannels", function(result, request, response) {
                try {
                    me.getChannels().then(channels => {
                        var res = {};
                        Object.values(channels).forEach(ch => {
                            ch.tag.seats = {};
                            ch.tag.ends = {};
                            Object.values(ch.seats).forEach(s => {
                                ch.tag.seats[s.kmsCode] = s.tag;
                            })
                            Object.values(ch.end).forEach(end => {
                                ch.tag.ends[end.kmsCode] = end.tag;
                                if (end.seat && end.seat != null) {
                                    end.tag.seatKmsId = end.seat.id;
                                    end.tag.seatCode = end.seat.tag.kmsCode;
                                }
                            });
                            res[ch.tag.kmsCode] = ch.tag;
                        })
                        result.data = res;
                        respJson(result, response);
                    });
                } catch (err) {
                    console.log(err);
                    respJson(result, response);
                }
            });
            webSvr.registRoute("kms", "v21", "startRecord", function(result, request, response) {
                try {
                    var srcId = result.ps.srcId;
                    var recFn = result.ps.recFn;
                    me.startRecord(srcId, recFn).then(res => {
                        delete res['recorder'];
                        result.data = res;
                        respJson(result, response);
                    });
                } catch (err) {
                    console.log(err);
                    respJson(result, response);
                }
            });
            webSvr.registRoute("kms", "v21", "stopRecord", function(result, request, response) {
                try {
                    var recorderId = result.ps.recorderId;
                    me.stopRecord(recorderId, recFn).then(res => {
                        result.data = res;
                        respJson(result, response);
                    });
                } catch (err) {
                    console.log(err);
                    respJson(result, response);
                }
            });
            webSvr.registRoute("kms", "v21", "connectMedia", function(result, request, response) {
                try {
                    var srcId = result.ps.srcId;
                    var targetId = result.ps.targetId;
                    var media = result.ps.mediaType;
                    me.connectMedia(srcId, targetId, media).then(res => {
                        result.data = res;
                        respJson(result, response);
                    });
                } catch (err) {
                    console.log(err);
                    respJson(result, response);
                }
            });
        }
    }
}
module.exports = {
    TKms: TKms
}

/*
var kms1 = new TKms();
var chCode = "chCode-102";
var seatCodes = ["s01", 's02', 's03', 's04'];


kms1.getClient().then(_ => {
    t = (new Date()).getTime();
    return kms1.getAllKmsObjs();
}).then(res => {
    return kms1.loadChannelSeats(chCode, seatCodes, true);
}).then(res => {
    return kms1.getAllKmsObjs();
}).then(objs => {
    console.log(Object.values(objs));
});
*/